//
//  UIWebView+JSCategory.m
//  jSCoreTest
//
//  Created by rain on 2018/6/26.
//  Copyright © 2018年 company. All rights reserved.
//

#import "UIWebView+JSCategory.h"

#import <objc/runtime.h>

//获取rainJavaScriptContext属性的键
static char rainJavaScriptContextKey[] = "rainJavaScriptContext";

//frame method
static char *webFrameBase64Str = "cGFyZW50RnJhbWU=";

static NSHashTable *webviews = nil;

@interface UIWebView(javaScriptCorePrivate)

/**
 自定义uiwebview私有方法对当前webview上获取到的jscontext处理

 @param context 当前webview的jscontext
 */
- (void)rainDidCreateJavaScriptContext:(JSContext *)context;

/**
 自定义uiwebview私有方法获取到当前的html的title

 @param title 获取到的html title
 */
- (void)rainDidReceiveTitle:(NSString *)title;


/**
 获取alert弹框

 @param message 消息内容
 */
- (void)rainRunJavaScriptAlertPanelWithMessage:(NSString *)message;


/**
 获取confirm弹框

 @param message 消息内容
 */
- (void)rainRunJavaScriptConfirmPanelWithMessage:(NSString *)message;

@end




@protocol RainWebFrame <NSObject>
- (id) parentFrame;
@end


@implementation NSObject (JSCategory)

/**
 其实是private api 通过监控这个方法是否调用实时知道jscontext是否创建；这个是依赖web上是否执行script，执行时会检查是否实现了下面的方法

 @param unused class 为WebView
 @param ctx 当前UIWebView运行的JSContext环境
 @param frame  WebFrameLoadDelegate   WebKit的WebFrame
 */
- (void) webView: (id) unused didCreateJavaScriptContext: (JSContext*) ctx forFrame: (id<RainWebFrame>) frame
{
    
    NSString *frameMehtod = [NSString stringWithUTF8String:webFrameBase64Str];
    NSData *frameData = [[NSData alloc]initWithBase64EncodedString:frameMehtod options:NSDataBase64DecodingIgnoreUnknownCharacters];
    NSString *frameStr = [[NSString alloc]initWithData:frameData encoding:NSUTF8StringEncoding];
    SEL frameSelector = NSSelectorFromString(frameStr);
    //     only interested in root-level frames
    if ([frame respondsToSelector:frameSelector] && [frame parentFrame] != nil ) {
        return;
    }
    
    void (^didCreateJavaScriptContext)() = ^{
        
        for (UIWebView *webv in webviews) {
            
            NSString *webIdentifier =  [NSString stringWithFormat:@"rain_jsWebView_%lud",(NSUInteger)webv.hash];
            
            NSString *jsAddVariable = [NSString stringWithFormat:@"var %@ = '%@'",webIdentifier,webIdentifier];
            
            [webv stringByEvaluatingJavaScriptFromString:jsAddVariable];//为webView添加个唯一标识符变量
            
            JSValue *identifierJSValue = ctx[webIdentifier];//通过这个获取当前的标识符的值[ctx objectForKeyedSubscript:webIdentifier];
            
            if ([identifierJSValue.toString isEqualToString:webIdentifier]) {
                [webv rainDidCreateJavaScriptContext:ctx];
                return ;
            }
            
        }
        
    };
    
    if ([NSThread isMainThread]) {
        didCreateJavaScriptContext();
    }else {
        dispatch_async(dispatch_get_main_queue(), didCreateJavaScriptContext);
        
    }
    
}

/**
 获取uiwebview当前链接的title

 @param unused class 为WebView
 @param title uiwebview当前链接的title
 @param frame  WebFrameLoadDelegate   WebKit的WebFrame
 */
- (void)webView:(id)unused didReceiveTitle:(NSString *)title forFrame:(id< RainWebFrame >)frame {
    
    void (^didReceiveTitle)(void) = ^{
        
        for (UIWebView *webv in webviews) {
            
            NSString *webIdentifier =  [NSString stringWithFormat:@"rain_jsWebView_%lud",(NSUInteger)webv.hash];
            
            [webv stringByEvaluatingJavaScriptFromString:[NSString stringWithFormat:@"var %@ = '%@'", webIdentifier, webIdentifier]];
            
            if ([webv.context[webIdentifier].toString isEqualToString:webIdentifier]) {
                [webv rainDidReceiveTitle:title];
                return;
            }
        }
    };
    
    if ([NSThread isMainThread]) {
        didReceiveTitle();
    }else {
    
        dispatch_async(dispatch_get_main_queue(), didReceiveTitle);
    
    }
    
}


/**
 监控html上的alert。如果不实现的话，就是调用h5的alert，实现的话，就会拦截h5的alert弹框

 @param unused class 为WebView
 @param message alert的消息内容
 @param frame  WebFrameLoadDelegate   WebKit的WebFrame
 */
/*
- (void)webView:(id)unused runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(id<RainWebFrame>)frame {
    
    
    void (^runJavaScriptAlertPanelWithMessage)(void) = ^{
        
        for (UIWebView *webv in webviews) {
            
            NSString *webIdentifier =  [NSString stringWithFormat:@"rain_jsWebView_%lud",(NSUInteger)webv.hash];
            
            [webv stringByEvaluatingJavaScriptFromString:[NSString stringWithFormat:@"var %@ = '%@'", webIdentifier, webIdentifier]];
            
            if ([webv.context[webIdentifier].toString isEqualToString:webIdentifier]) {
                
                UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:[self javascriptAlertTitleForWebView:webv]
                                                                    message:message
                                                                   delegate:nil
                                                          cancelButtonTitle:@"确定"
                                                          otherButtonTitles:nil];
                [alertView show];
                
                
                [webv rainRunJavaScriptAlertPanelWithMessage:message];
                
                return;
            }
        }
    };
    
    dispatch_async(dispatch_get_main_queue(), runJavaScriptAlertPanelWithMessage);
    
}
*/
/**
 监控confirm的弹框内容

 @param unused class 为WebView
 @param message confirm的消息内容
 @param frame  WebFrameLoadDelegate   WebKit的WebFrame
 @return bool
 */
- (BOOL)webView:(id)unused runJavaScriptConfirmPanelWithMessage:(NSString *)message initiatedByFrame:(id<RainWebFrame>)frame {
    void (^runJavaScriptAlertPanelWithMessage)(void) = ^{
        
        for (UIWebView *webv in webviews) {
            
            NSString *webIdentifier =  [NSString stringWithFormat:@"rain_jsWebView_%lud",(NSUInteger)webv.hash];
            
            [webv stringByEvaluatingJavaScriptFromString:[NSString stringWithFormat:@"var %@ = '%@'", webIdentifier, webIdentifier]];
            
            if ([webv.context[webIdentifier].toString isEqualToString:webIdentifier]) {
                /*
                UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:[self javascriptAlertTitleForWebView:webv]
                                                                    message:message
                                                                   delegate:nil
                                                          cancelButtonTitle:@"取消"
                                                          otherButtonTitles:@"确定", nil];
                [alertView show];
                */
                //这里可以不用弹出这些内容
                
                [webv rainRunJavaScriptConfirmPanelWithMessage:message];
                
                return;
            }
        }
    };
    
    dispatch_async(dispatch_get_main_queue(), runJavaScriptAlertPanelWithMessage);
    
    return NO;
    
}

- (NSString *)javascriptAlertTitleForWebView:(UIWebView *)webView {
    NSURL *url = [[webView request] URL];
    NSString *scheme = [url scheme];
    NSString *host = [url host];
    NSMutableString *mstring = [NSMutableString string];
    if ([scheme length] > 0) {
        [mstring appendString:scheme];
        [mstring appendString:@"://"];
    }
    if ([host length] > 0) {
        [mstring appendString:host];
    }
    
    return mstring;
}

@end



@implementation UIWebView (JSCategory)
+ (id) allocWithZone:(struct _NSZone *)zone
{
    static dispatch_once_t onceToken;
    
    dispatch_once(&onceToken, ^{
        
        webviews = [NSHashTable weakObjectsHashTable];
        
    });
    
    NSAssert( [NSThread isMainThread], @"uh oh - why aren't we on the main thread?");
    
    id webView = [super allocWithZone: zone];
    
    [webviews addObject: webView];
    
    return webView;
}

- (void)rainDidCreateJavaScriptContext:(JSContext *)context {
    
    [self willChangeValueForKey: [NSString stringWithUTF8String:rainJavaScriptContextKey]];
    
    objc_setAssociatedObject(self, rainJavaScriptContextKey, context, OBJC_ASSOCIATION_RETAIN);
    
    [self didChangeValueForKey:[NSString stringWithUTF8String:rainJavaScriptContextKey]];
    
    
    if ([self.delegate respondsToSelector:@selector(webView:didCreateJavaScriptContext:)]) {
        
        id<RainWebViewDelegate>delegate = (id<RainWebViewDelegate>)self.delegate;
        
        [delegate webView:self didCreateJavaScriptContext:context];
        
    }
    
}

- (JSContext *)context {
    
    return objc_getAssociatedObject(self, rainJavaScriptContextKey);
    
}

- (void)rainDidReceiveTitle:(NSString *)title {
    if ([self.delegate respondsToSelector:@selector(webView:didReceiveTitle:forFrame:)]) {
        id<RainWebViewDelegate> delegate = (id<RainWebViewDelegate>)self.delegate;
        [delegate webView:self didReceiveTitle:title];
    }
    
}

- (void)rainRunJavaScriptAlertPanelWithMessage:(NSString *)message {
    
    if ([self.delegate respondsToSelector:@selector(webView:runJavaScriptAlertPanelWithMessage:)]) {
        id<RainWebViewDelegate> delegate = (id<RainWebViewDelegate>)self.delegate;
        [delegate webView:self runJavaScriptAlertPanelWithMessage:message];
    }
}

- (void)rainRunJavaScriptConfirmPanelWithMessage:(NSString *)message {
    
    if ([self.delegate respondsToSelector:@selector(webView:runJavaScriptAlertPanelWithMessage:)]) {
        id<RainWebViewDelegate> delegate = (id<RainWebViewDelegate>)self.delegate;
        [delegate webView:self runJavaScriptAlertPanelWithMessage:message];
    }
}


@end
